agora inbox for pgsql-hackers@postgresql.orghelp / color / mirror / Atom feed
[PATCH v32 10/11] Add regression tests for Incremental View Maintenance 604+ messages / 2 participants [nested] [flat]
* [PATCH v32 10/11] Add regression tests for Incremental View Maintenance @ 2021-03-10 02:11 Takuma Hoshiai <takuma.hoshiai@gmail.com> 0 siblings, 0 replies; 604+ messages in thread From: Takuma Hoshiai @ 2021-03-10 02:11 UTC (permalink / raw) --- .../regress/expected/incremental_matview.out | 1030 +++++++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/incremental_matview.sql | 533 +++++++++ 3 files changed, 1564 insertions(+), 1 deletion(-) create mode 100644 src/test/regress/expected/incremental_matview.out create mode 100644 src/test/regress/sql/incremental_matview.sql diff --git a/src/test/regress/expected/incremental_matview.out b/src/test/regress/expected/incremental_matview.out new file mode 100644 index 0000000000..d65896425e --- /dev/null +++ b/src/test/regress/expected/incremental_matview.out @@ -0,0 +1,1030 @@ +-- create a table to use as a basis for views and materialized views in various combinations +CREATE TABLE mv_base_a (i int, j int); +INSERT INTO mv_base_a VALUES + (1,10), + (2,20), + (3,30), + (4,40), + (5,50); +CREATE TABLE mv_base_b (i int, k int); +INSERT INTO mv_base_b VALUES + (1,101), + (2,102), + (3,103), + (4,104); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +ERROR: materialized view "mv_ivm_1" has not been populated +HINT: Use the REFRESH MATERIALIZED VIEW command. +REFRESH MATERIALIZED VIEW mv_ivm_1; +NOTICE: could not create an index on materialized view "mv_ivm_1" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 10 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 +(4 rows) + +-- REFRESH WITH NO DATA +BEGIN; +CREATE FUNCTION dummy_ivm_trigger_func() RETURNS TRIGGER AS $$ + BEGIN + RETURN NULL; + END +$$ language plpgsql; +CREATE CONSTRAINT TRIGGER dummy_ivm_trigger AFTER INSERT +ON mv_base_a FROM mv_ivm_1 FOR EACH ROW +EXECUTE PROCEDURE dummy_ivm_trigger_func(); +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; + count +------- + 17 +(1 row) + +REFRESH MATERIALIZED VIEW mv_ivm_1 WITH NO DATA; +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; + count +------- + 1 +(1 row) + +ROLLBACK; +-- immediate maintenance +BEGIN; +INSERT INTO mv_base_b VALUES(5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 10 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 + 5 | 50 | 105 +(5 rows) + +UPDATE mv_base_a SET j = 0 WHERE i = 1; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 0 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 + 5 | 50 | 105 +(5 rows) + +DELETE FROM mv_base_b WHERE (i,k) = (5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 0 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 +(4 rows) + +ROLLBACK; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + i | j | k +---+----+----- + 1 | 10 | 101 + 2 | 20 | 102 + 3 | 30 | 103 + 4 | 40 | 104 +(4 rows) + +-- rename of IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_rename AS SELECT DISTINCT * FROM mv_base_a; +NOTICE: created index "mv_ivm_rename_index" on materialized view "mv_ivm_rename" +ALTER MATERIALIZED VIEW mv_ivm_rename RENAME COLUMN __ivm_count__ TO xxx; +ERROR: IVM column can not be renamed +DROP MATERIALIZED VIEW mv_ivm_rename; +-- unique index on IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_unique AS SELECT DISTINCT * FROM mv_base_a; +NOTICE: created index "mv_ivm_unique_index" on materialized view "mv_ivm_unique" +CREATE UNIQUE INDEX ON mv_ivm_unique(__ivm_count__); +ERROR: unique index creation on IVM columns is not supported +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__)); +ERROR: unique index creation on IVM columns is not supported +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__ + 1)); +ERROR: unique index creation on IVM columns is not supported +DROP MATERIALIZED VIEW mv_ivm_unique; +-- TRUNCATE a base table in join views +BEGIN; +TRUNCATE mv_base_a; +SELECT * FROM mv_ivm_1; + i | j | k +---+---+--- +(0 rows) + +ROLLBACK; +BEGIN; +TRUNCATE mv_base_b; +SELECT * FROM mv_ivm_1; + i | j | k +---+---+--- +(0 rows) + +ROLLBACK; +-- some query syntax +BEGIN; +CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql' + AS 'SELECT 1' IMMUTABLE; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_func AS SELECT * FROM ivm_func(); +NOTICE: could not create an index on materialized view "mv_ivm_func" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_no_tbl AS SELECT 1; +NOTICE: could not create an index on materialized view "mv_ivm_no_tbl" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +ROLLBACK; +-- result of materialized view have DISTINCT clause or the duplicate result. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_duplicate AS SELECT j FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_duplicate" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_distinct AS SELECT DISTINCT j FROM mv_base_a; +NOTICE: created index "mv_ivm_distinct_index" on materialized view "mv_ivm_distinct" +INSERT INTO mv_base_a VALUES(6,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; + j +---- + 10 + 20 + 20 + 30 + 40 + 50 +(6 rows) + +SELECT * FROM mv_ivm_distinct ORDER BY 1; + j +---- + 10 + 20 + 30 + 40 + 50 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; + j +---- + 10 + 20 + 30 + 40 + 50 +(5 rows) + +SELECT * FROM mv_ivm_distinct ORDER BY 1; + j +---- + 10 + 20 + 30 + 40 + 50 +(5 rows) + +ROLLBACK; +-- support SUM(), COUNT() and AVG() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 120 | 2 | 60.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +UPDATE mv_base_a SET j = 200 WHERE (i,j) = (2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+---------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 220 | 2 | 110.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) = (2,200); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +ROLLBACK; +-- support COUNT(*) aggregate function +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; + i | sum | count +---+-----+------- + 1 | 10 | 1 + 2 | 20 | 1 + 3 | 30 | 1 + 4 | 40 | 1 + 5 | 50 | 1 +(5 rows) + +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; + i | sum | count +---+-----+------- + 1 | 10 | 1 + 2 | 120 | 2 + 3 | 30 | 1 + 4 | 40 | 1 + 5 | 50 | 1 +(5 rows) + +ROLLBACK; +-- TRUNCATE a base table in aggregate views +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +TRUNCATE mv_base_a; +SELECT sum, count FROM mv_ivm_agg; + sum | count +-----+------- +(0 rows) + +SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; + i | sum | count +---+-----+------- +(0 rows) + +ROLLBACK; +-- support aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_group" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_group ORDER BY 1; + sum | count | avg +-----+-------+--------------------- + 150 | 5 | 30.0000000000000000 +(1 row) + +INSERT INTO mv_base_a VALUES(6,60); +SELECT * FROM mv_ivm_group ORDER BY 1; + sum | count | avg +-----+-------+--------------------- + 210 | 6 | 35.0000000000000000 +(1 row) + +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_group ORDER BY 1; + sum | count | avg +-----+-------+----- + | 0 | +(1 row) + +ROLLBACK; +-- TRUNCATE a base table in aggregate views without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_group" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +TRUNCATE mv_base_a; +SELECT sum, count, avg FROM mv_ivm_group; + sum | count | avg +-----+-------+----- + | 0 | +(1 row) + +SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; + sum | count | avg +-----+-------+----- + | 0 | +(1 row) + +ROLLBACK; +-- resolved issue: When use AVG() function and values is indivisible, result of AVG() is incorrect. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_avg_bug AS SELECT i, SUM(j), COUNT(j), AVG(j) FROM mv_base_A GROUP BY i; +NOTICE: created index "mv_ivm_avg_bug_index" on materialized view "mv_ivm_avg_bug" +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +INSERT INTO mv_base_a VALUES + (1,0), + (1,0), + (2,30), + (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 3 | 3.3333333333333333 + 2 | 80 | 3 | 26.6666666666666667 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) = (1,0); +DELETE FROM mv_base_a WHERE (i,j) = (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; + i | sum | count | avg +---+-----+-------+--------------------- + 1 | 10 | 1 | 10.0000000000000000 + 2 | 20 | 1 | 20.0000000000000000 + 3 | 30 | 1 | 30.0000000000000000 + 4 | 40 | 1 | 40.0000000000000000 + 5 | 50 | 1 | 50.0000000000000000 +(5 rows) + +ROLLBACK; +-- support MIN(), MAX() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_min_max_index" on materialized view "mv_ivm_min_max" +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; + i | min | max +---+-----+----- + 1 | 10 | 10 + 2 | 20 | 20 + 3 | 30 | 30 + 4 | 40 | 40 + 5 | 50 | 50 +(5 rows) + +INSERT INTO mv_base_a VALUES + (1,11), (1,12), + (2,21), (2,22), + (3,31), (3,32), + (4,41), (4,42), + (5,51), (5,52); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; + i | min | max +---+-----+----- + 1 | 10 | 12 + 2 | 20 | 22 + 3 | 30 | 32 + 4 | 40 | 42 + 5 | 50 | 52 +(5 rows) + +DELETE FROM mv_base_a WHERE (i,j) IN ((1,10), (2,21), (3,32)); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; + i | min | max +---+-----+----- + 1 | 11 | 12 + 2 | 20 | 22 + 3 | 30 | 31 + 4 | 40 | 42 + 5 | 50 | 52 +(5 rows) + +ROLLBACK; +-- support MIN(), MAX() aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT MIN(j), MAX(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_min_max" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + 10 | 50 +(1 row) + +INSERT INTO mv_base_a VALUES + (0,0), (6,60), (7,70); +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + 0 | 70 +(1 row) + +DELETE FROM mv_base_a WHERE (i,j) IN ((0,0), (7,70)); +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + 10 | 60 +(1 row) + +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_min_max; + min | max +-----+----- + | +(1 row) + +ROLLBACK; +-- Test MIN/MAX after search_path change +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min AS SELECT MIN(j) FROM mv_base_a; +NOTICE: could not create an index on materialized view "mv_ivm_min" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_ivm_min; + min +----- + 10 +(1 row) + +CREATE SCHEMA myschema; +GRANT ALL ON SCHEMA myschema TO public; +CREATE TABLE myschema.mv_base_a (j int); +INSERT INTO myschema.mv_base_a VALUES (1); +DELETE FROM mv_base_a WHERE (i,j) = (1,10); +SELECT * FROM mv_ivm_min; + min +----- + 20 +(1 row) + +SET search_path TO myschema,public,pg_catalog; +DELETE FROM public.mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_min; + min +----- + 30 +(1 row) + +ROLLBACK; +-- aggregate views with column names specified +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; + a | sum +---+------ + 1 | 110 + 2 | 2200 + 3 | 300 + 4 | 40 + 5 | 50 +(5 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +NOTICE: created index "mv_ivm_agg_index" on materialized view "mv_ivm_agg" +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; + a | b +---+------ + 1 | 110 + 2 | 2200 + 3 | 300 + 4 | 40 + 5 | 50 +(5 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b,c) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +ERROR: too many column names were specified +ROLLBACK; +-- support self join view and multiple change on the same table +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30); +CREATE INCREMENTAL MATERIALIZED VIEW mv_self(v1, v2) AS + SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i; +NOTICE: could not create an index on materialized view "mv_self" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv_self ORDER BY v1; + v1 | v2 +----+---- + 10 | 10 + 20 | 20 + 30 | 30 +(3 rows) + +INSERT INTO base_t VALUES (4,40); +DELETE FROM base_t WHERE i = 1; +UPDATE base_t SET v = v*10 WHERE i=2; +SELECT * FROM mv_self ORDER BY v1; + v1 | v2 +-----+----- + 30 | 30 + 40 | 40 + 200 | 200 +(3 rows) + +WITH + ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1), + ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1), + upd_t AS (UPDATE base_t SET v = v + 100 RETURNING 1), + dlt_t AS (DELETE FROM base_t WHERE i IN (4,5) RETURNING 1) +SELECT NULL; + ?column? +---------- + +(1 row) + +SELECT * FROM mv_self ORDER BY v1; + v1 | v2 +-----+----- + 50 | 50 + 60 | 60 + 130 | 130 + 300 | 300 +(4 rows) + +--- with sub-transactions +SAVEPOINT p1; +INSERT INTO base_t VALUES (7,70); +RELEASE SAVEPOINT p1; +INSERT INTO base_t VALUES (7,77); +SELECT * FROM mv_self ORDER BY v1, v2; + v1 | v2 +-----+----- + 50 | 50 + 60 | 60 + 70 | 70 + 70 | 77 + 77 | 70 + 77 | 77 + 130 | 130 + 300 | 300 +(8 rows) + +ROLLBACK; +-- support simultaneous table changes +BEGIN; +CREATE TABLE base_r (i int, v int); +CREATE TABLE base_s (i int, v int); +INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30); +INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300); +CREATE INCREMENTAL MATERIALIZED VIEW mv(v1, v2) AS + SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i); +NOTICE: could not create an index on materialized view "mv" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv ORDER BY v1; + v1 | v2 +----+----- + 10 | 100 + 20 | 200 + 30 | 300 +(3 rows) + +WITH + ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1), + ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1), + ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1), + upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1), + dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1) +SELECT NULL; + ?column? +---------- + +(1 row) + +SELECT * FROM mv ORDER BY v1; + v1 | v2 +------+----- + 10 | 100 + 11 | 100 + 1020 | 200 + 1020 | 222 +(4 rows) + +ROLLBACK; +-- support foreign reference constraints +BEGIN; +CREATE TABLE ri1 (i int PRIMARY KEY); +CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int); +INSERT INTO ri1 VALUES (1),(2),(3); +INSERT INTO ri2 VALUES (1),(2),(3); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ri(i1, i2) AS + SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i); +NOTICE: created index "mv_ri_index" on materialized view "mv_ri" +SELECT * FROM mv_ri ORDER BY i1; + i1 | i2 +----+---- + 1 | 1 + 2 | 2 + 3 | 3 +(3 rows) + +UPDATE ri1 SET i=10 where i=1; +DELETE FROM ri1 WHERE i=2; +SELECT * FROM mv_ri ORDER BY i2; + i1 | i2 +----+---- + 3 | 3 + 10 | 10 +(2 rows) + +ROLLBACK; +-- views including NULL +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1,10),(2, NULL); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +NOTICE: could not create an index on materialized view "mv" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv ORDER BY i; + i | v +---+---- + 1 | 10 + 2 | +(2 rows) + +UPDATE base_t SET v = 20 WHERE i = 2; +SELECT * FROM mv ORDER BY i; + i | v +---+---- + 1 | 10 + 2 | 20 +(2 rows) + +ROLLBACK; +BEGIN; +CREATE TABLE base_t (i int); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +NOTICE: could not create an index on materialized view "mv" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT * FROM mv ORDER BY i; + i +--- +(0 rows) + +INSERT INTO base_t VALUES (1),(NULL); +SELECT * FROM mv ORDER BY i; + i +--- + 1 + +(2 rows) + +ROLLBACK; +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (1, 10), (1, 20); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, sum(v) FROM base_t GROUP BY i; +NOTICE: created index "mv_index" on materialized view "mv" +SELECT * FROM mv ORDER BY i; + i | sum +---+----- + 1 | 30 + | 3 +(2 rows) + +UPDATE base_t SET v = v * 10; +SELECT * FROM mv ORDER BY i; + i | sum +---+----- + 1 | 300 + | 30 +(2 rows) + +ROLLBACK; +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (NULL, 3), (NULL, 4), (NULL, 5); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, min(v), max(v) FROM base_t GROUP BY i; +NOTICE: created index "mv_index" on materialized view "mv" +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 1 | 5 +(1 row) + +DELETE FROM base_t WHERE v = 1; +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 2 | 5 +(1 row) + +DELETE FROM base_t WHERE v = 3; +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 2 | 5 +(1 row) + +DELETE FROM base_t WHERE v = 5; +SELECT * FROM mv ORDER BY i; + i | min | max +---+-----+----- + | 2 | 4 +(1 row) + +ROLLBACK; +-- IMMV containing user defined type +BEGIN; +CREATE TYPE mytype; +CREATE FUNCTION mytype_in(cstring) + RETURNS mytype AS 'int4in' + LANGUAGE INTERNAL STRICT IMMUTABLE; +NOTICE: return type mytype is only a shell +CREATE FUNCTION mytype_out(mytype) + RETURNS cstring AS 'int4out' + LANGUAGE INTERNAL STRICT IMMUTABLE; +NOTICE: argument type mytype is only a shell +CREATE TYPE mytype ( + LIKE = int4, + INPUT = mytype_in, + OUTPUT = mytype_out +); +CREATE FUNCTION mytype_eq(mytype, mytype) + RETURNS bool AS 'int4eq' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_lt(mytype, mytype) + RETURNS bool AS 'int4lt' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_cmp(mytype, mytype) + RETURNS integer AS 'btint4cmp' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE OPERATOR = ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_eq); +CREATE OPERATOR < ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_lt); +CREATE OPERATOR CLASS mytype_ops + DEFAULT FOR TYPE mytype USING btree AS + OPERATOR 1 <, + OPERATOR 3 = , + FUNCTION 1 mytype_cmp(mytype,mytype); +CREATE TABLE t_mytype (x mytype); +CREATE INCREMENTAL MATERIALIZED VIEW mv_mytype AS + SELECT * FROM t_mytype; +NOTICE: could not create an index on materialized view "mv_mytype" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +INSERT INTO t_mytype VALUES ('1'::mytype); +SELECT * FROM mv_mytype; + x +--- + 1 +(1 row) + +ROLLBACK; +-- outer join is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i; +ERROR: OUTER JOIN is not supported on incrementally maintainable materialized view +-- CTE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i; +ERROR: CTE is not supported on incrementally maintainable materialized view +-- contain system column +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm01 AS SELECT i,j,xmin FROM mv_base_a; +ERROR: system column is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm02 AS SELECT i,j FROM mv_base_a WHERE xmin = '610'; +ERROR: system column is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT i,j,xmin::text AS x_min FROM mv_base_a; +ERROR: system column is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm06 AS SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a; +ERROR: system column is not supported on incrementally maintainable materialized view +-- contain subquery +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 ); +ERROR: subquery is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i; +ERROR: subquery is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a; +ERROR: subquery is not supported on incrementally maintainable materialized view +-- contain ORDER BY +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k; +ERROR: ORDER BY clause is not supported on incrementally maintainable materialized view +-- contain HAVING +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5; +ERROR: HAVING clause is not supported on incrementally maintainable materialized view +-- contain view or materialized view +CREATE VIEW b_view AS SELECT i,k FROM mv_base_b; +CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i; +ERROR: VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i; +ERROR: VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm09 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i; +ERROR: subquery is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm10 AS SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 5; +ERROR: subquery is not supported on incrementally maintainable materialized view +-- contain mutable functions +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm12 AS SELECT i,j FROM mv_base_a WHERE i = random()::int; +ERROR: mutable function is not supported on incrementally maintainable materialized view +HINT: functions must be marked IMMUTABLE +-- LIMIT/OFFSET is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm13 AS SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5; +ERROR: LIMIT/OFFSET clause is not supported on incrementally maintainable materialized view +-- DISTINCT ON is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm14 AS SELECT DISTINCT ON(i) i, j FROM mv_base_a; +ERROR: DISTINCT ON is not supported on incrementally maintainable materialized view +-- TABLESAMPLE clause is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm15 AS SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50); +ERROR: TABLESAMPLE clause is not supported on incrementally maintainable materialized view +-- window functions are not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm16 AS SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a; +ERROR: window functions are not supported on incrementally maintainable materialized view +-- aggregate function with some options is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm17 AS SELECT COUNT(*) FILTER(WHERE i < 3) FROM mv_base_a; +ERROR: aggregate function with FILTER clause is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm18 AS SELECT COUNT(DISTINCT i) FROM mv_base_a; +ERROR: aggregate function with DISTINCT arguments is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm19 AS SELECT array_agg(j ORDER BY i DESC) FROM mv_base_a; +ERROR: aggregate function with ORDER clause is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm20 AS SELECT i,SUM(j) FROM mv_base_a GROUP BY GROUPING SETS((i),()); +ERROR: GROUPING SETS, ROLLUP, or CUBE clauses is not supported on incrementally maintainable materialized view +-- inheritance parent is not supported +BEGIN; +CREATE TABLE parent (i int, v int); +CREATE TABLE child_a(options text) INHERITS(parent); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm21 AS SELECT * FROM parent; +ERROR: inheritance parent is not supported on incrementally maintainable materialized view +ROLLBACK; +-- UNION statement is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm22 AS SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b;; +ERROR: UNION/INTERSECT/EXCEPT statements are not supported on incrementally maintainable materialized view +-- empty target list is not allowed with IVM +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm25 AS SELECT FROM mv_base_a; +ERROR: empty target list is not supported on incrementally maintainable materialized view +-- FOR UPDATE/SHARE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm26 AS SELECT i,j FROM mv_base_a FOR UPDATE; +ERROR: FOR UPDATE/SHARE clause is not supported on incrementally maintainable materialized view +-- tartget list cannot contain ivm column that start with '__ivm' +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm28 AS SELECT i AS "__ivm_count__" FROM mv_base_a; +ERROR: column name __ivm_count__ is not supported on incrementally maintainable materialized view +-- expressions specified in GROUP BY must appear in the target list. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm29 AS SELECT COUNT(i) FROM mv_base_a GROUP BY i; +ERROR: GROUP BY expression not appearing in select list is not supported on incrementally maintainable materialized view +-- experssions containing an aggregate is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm30 AS SELECT sum(i)*0.5 FROM mv_base_a; +ERROR: expression containing an aggregate in it is not supported on incrementally maintainable materialized view +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm31 AS SELECT sum(i)/sum(j) FROM mv_base_a; +ERROR: expression containing an aggregate in it is not supported on incrementally maintainable materialized view +-- VALUES is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_only_values1 AS values(1); +ERROR: VALUES is not supported on incrementally maintainable materialized view +-- views containing base tables with Row Level Security +DROP USER IF EXISTS regress_ivm_admin; +NOTICE: role "regress_ivm_admin" does not exist, skipping +DROP USER IF EXISTS regress_ivm_user; +NOTICE: role "regress_ivm_user" does not exist, skipping +CREATE USER regress_ivm_admin; +CREATE USER regress_ivm_user; +--- create a table with RLS +SET SESSION AUTHORIZATION regress_ivm_admin; +CREATE TABLE rls_tbl(id int, data text, owner name); +INSERT INTO rls_tbl VALUES + (1,'foo','regress_ivm_user'), + (2,'bar','postgres'); +CREATE TABLE num_tbl(id int, num text); +INSERT INTO num_tbl VALUES + (1,'one'), + (2,'two'), + (3,'three'), + (4,'four'), + (5,'five'), + (6,'six'); +--- Users can access only their own rows +CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user); +ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; +GRANT ALL on rls_tbl TO PUBLIC; +GRANT ALL on num_tbl TO PUBLIC; +--- create a view owned by regress_ivm_user +SET SESSION AUTHORIZATION regress_ivm_user; +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls AS SELECT * FROM rls_tbl; +NOTICE: could not create an index on materialized view "ivm_rls" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + id | data | owner +----+------+------------------ + 1 | foo | regress_ivm_user +(1 row) + +RESET SESSION AUTHORIZATION; +--- inserts rows owned by different users +INSERT INTO rls_tbl VALUES + (3,'baz','regress_ivm_user'), + (4,'qux','postgres'); +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + id | data | owner +----+------+------------------ + 1 | foo | regress_ivm_user + 3 | baz | regress_ivm_user +(2 rows) + +--- combination of diffent kinds of commands +WITH + i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')), + u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1), + u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2) +SELECT; +-- +(1 row) + +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + id | data | owner +----+-------+------------------ + 2 | bar | regress_ivm_user + 3 | baz | regress_ivm_user + 6 | corge | regress_ivm_user +(3 rows) + +--- +SET SESSION AUTHORIZATION regress_ivm_user; +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls2 AS SELECT * FROM rls_tbl JOIN num_tbl USING(id); +NOTICE: could not create an index on materialized view "ivm_rls2" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +RESET SESSION AUTHORIZATION; +WITH + x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)), + y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4)) +SELECT; +-- +(1 row) + +SELECT * FROM ivm_rls2 ORDER BY 1,2,3; + id | data | owner | num +----+-------+------------------+--------- + 2 | bar | regress_ivm_user | two + 3 | baz_2 | regress_ivm_user | three_2 + 6 | corge | regress_ivm_user | six +(3 rows) + +-- automatic index creation +CREATE TABLE base_a (i int primary key, j int); +CREATE TABLE base_b (i int primary key, j int); +--- group by: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx1 AS SELECT i, sum(j) FROM base_a GROUP BY i; +NOTICE: created index "mv_idx1_index" on materialized view "mv_idx1" +--- distinct: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx2 AS SELECT DISTINCT j FROM base_a; +NOTICE: created index "mv_idx2_index" on materialized view "mv_idx2" +--- with all pkey columns: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx3(i_a, i_b) AS SELECT a.i, b.i FROM base_a a, base_b b; +NOTICE: created index "mv_idx3_index" on materialized view "mv_idx3" +--- missing some pkey columns: no index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx4 AS SELECT j FROM base_a; +NOTICE: could not create an index on materialized view "mv_idx4" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx5 AS SELECT a.i, b.j FROM base_a a, base_b b; +NOTICE: could not create an index on materialized view "mv_idx5" automatically +DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause. +HINT: Create an index on the materialized view for efficient incremental maintenance. +-- cleanup +DROP TABLE rls_tbl CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to materialized view ivm_rls +drop cascades to materialized view ivm_rls2 +DROP TABLE num_tbl CASCADE; +DROP USER regress_ivm_user; +DROP USER regress_ivm_admin; +DROP TABLE mv_base_b CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to materialized view mv_ivm_1 +drop cascades to view b_view +drop cascades to materialized view b_mview +DROP TABLE mv_base_a CASCADE; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 5ac6e871f5..64c910af65 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -78,7 +78,7 @@ test: brin_bloom brin_multi # psql depends on create_am # amutils depends on geometry, create_index_spgist, hash_index, brin # ---------- -test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps +test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps incremental_matview # collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 diff --git a/src/test/regress/sql/incremental_matview.sql b/src/test/regress/sql/incremental_matview.sql new file mode 100644 index 0000000000..90116edff8 --- /dev/null +++ b/src/test/regress/sql/incremental_matview.sql @@ -0,0 +1,533 @@ +-- create a table to use as a basis for views and materialized views in various combinations +CREATE TABLE mv_base_a (i int, j int); +INSERT INTO mv_base_a VALUES + (1,10), + (2,20), + (3,30), + (4,40), + (5,50); +CREATE TABLE mv_base_b (i int, k int); +INSERT INTO mv_base_b VALUES + (1,101), + (2,102), + (3,103), + (4,104); + +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_1 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) WITH NO DATA; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +REFRESH MATERIALIZED VIEW mv_ivm_1; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + +-- REFRESH WITH NO DATA +BEGIN; +CREATE FUNCTION dummy_ivm_trigger_func() RETURNS TRIGGER AS $$ + BEGIN + RETURN NULL; + END +$$ language plpgsql; + +CREATE CONSTRAINT TRIGGER dummy_ivm_trigger AFTER INSERT +ON mv_base_a FROM mv_ivm_1 FOR EACH ROW +EXECUTE PROCEDURE dummy_ivm_trigger_func(); + +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; + +REFRESH MATERIALIZED VIEW mv_ivm_1 WITH NO DATA; + +SELECT COUNT(*) +FROM pg_depend pd INNER JOIN pg_trigger pt ON pd.objid = pt.oid +WHERE pd.classid = 'pg_trigger'::regclass AND pd.refobjid = 'mv_ivm_1'::regclass; +ROLLBACK; + +-- immediate maintenance +BEGIN; +INSERT INTO mv_base_b VALUES(5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +UPDATE mv_base_a SET j = 0 WHERE i = 1; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +DELETE FROM mv_base_b WHERE (i,k) = (5,105); +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; +ROLLBACK; +SELECT * FROM mv_ivm_1 ORDER BY 1,2,3; + +-- rename of IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_rename AS SELECT DISTINCT * FROM mv_base_a; +ALTER MATERIALIZED VIEW mv_ivm_rename RENAME COLUMN __ivm_count__ TO xxx; +DROP MATERIALIZED VIEW mv_ivm_rename; + +-- unique index on IVM columns +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_unique AS SELECT DISTINCT * FROM mv_base_a; +CREATE UNIQUE INDEX ON mv_ivm_unique(__ivm_count__); +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__)); +CREATE UNIQUE INDEX ON mv_ivm_unique((__ivm_count__ + 1)); +DROP MATERIALIZED VIEW mv_ivm_unique; + +-- TRUNCATE a base table in join views +BEGIN; +TRUNCATE mv_base_a; +SELECT * FROM mv_ivm_1; +ROLLBACK; + +BEGIN; +TRUNCATE mv_base_b; +SELECT * FROM mv_ivm_1; +ROLLBACK; + +-- some query syntax +BEGIN; +CREATE FUNCTION ivm_func() RETURNS int LANGUAGE 'sql' + AS 'SELECT 1' IMMUTABLE; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_func AS SELECT * FROM ivm_func(); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_no_tbl AS SELECT 1; +ROLLBACK; + +-- result of materialized view have DISTINCT clause or the duplicate result. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_duplicate AS SELECT j FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_distinct AS SELECT DISTINCT j FROM mv_base_a; +INSERT INTO mv_base_a VALUES(6,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; +SELECT * FROM mv_ivm_distinct ORDER BY 1; +DELETE FROM mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_duplicate ORDER BY 1; +SELECT * FROM mv_ivm_distinct ORDER BY 1; +ROLLBACK; + +-- support SUM(), COUNT() and AVG() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(i), AVG(j) FROM mv_base_a GROUP BY i; +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +UPDATE mv_base_a SET j = 200 WHERE (i,j) = (2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +DELETE FROM mv_base_a WHERE (i,j) = (2,200); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3,4; +ROLLBACK; + +-- support COUNT(*) aggregate function +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; +INSERT INTO mv_base_a VALUES(2,100); +SELECT * FROM mv_ivm_agg ORDER BY 1,2,3; +ROLLBACK; + +-- TRUNCATE a base table in aggregate views +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg AS SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +TRUNCATE mv_base_a; +SELECT sum, count FROM mv_ivm_agg; +SELECT i, SUM(j), COUNT(*) FROM mv_base_a GROUP BY i; +ROLLBACK; + +-- support aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +SELECT * FROM mv_ivm_group ORDER BY 1; +INSERT INTO mv_base_a VALUES(6,60); +SELECT * FROM mv_ivm_group ORDER BY 1; +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_group ORDER BY 1; +ROLLBACK; + +-- TRUNCATE a base table in aggregate views without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_group AS SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +TRUNCATE mv_base_a; +SELECT sum, count, avg FROM mv_ivm_group; +SELECT SUM(j), COUNT(j), AVG(j) FROM mv_base_a; +ROLLBACK; + +-- resolved issue: When use AVG() function and values is indivisible, result of AVG() is incorrect. +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_avg_bug AS SELECT i, SUM(j), COUNT(j), AVG(j) FROM mv_base_A GROUP BY i; +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; +INSERT INTO mv_base_a VALUES + (1,0), + (1,0), + (2,30), + (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; +DELETE FROM mv_base_a WHERE (i,j) = (1,0); +DELETE FROM mv_base_a WHERE (i,j) = (2,30); +SELECT * FROM mv_ivm_avg_bug ORDER BY 1,2,3; +ROLLBACK; + +-- support MIN(), MAX() aggregate functions +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT i, MIN(j), MAX(j) FROM mv_base_a GROUP BY i; +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; +INSERT INTO mv_base_a VALUES + (1,11), (1,12), + (2,21), (2,22), + (3,31), (3,32), + (4,41), (4,42), + (5,51), (5,52); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; +DELETE FROM mv_base_a WHERE (i,j) IN ((1,10), (2,21), (3,32)); +SELECT * FROM mv_ivm_min_max ORDER BY 1,2,3; +ROLLBACK; + +-- support MIN(), MAX() aggregate functions without GROUP clause +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min_max AS SELECT MIN(j), MAX(j) FROM mv_base_a; +SELECT * FROM mv_ivm_min_max; +INSERT INTO mv_base_a VALUES + (0,0), (6,60), (7,70); +SELECT * FROM mv_ivm_min_max; +DELETE FROM mv_base_a WHERE (i,j) IN ((0,0), (7,70)); +SELECT * FROM mv_ivm_min_max; +DELETE FROM mv_base_a; +SELECT * FROM mv_ivm_min_max; +ROLLBACK; + +-- Test MIN/MAX after search_path change +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_min AS SELECT MIN(j) FROM mv_base_a; +SELECT * FROM mv_ivm_min; + +CREATE SCHEMA myschema; +GRANT ALL ON SCHEMA myschema TO public; +CREATE TABLE myschema.mv_base_a (j int); +INSERT INTO myschema.mv_base_a VALUES (1); + +DELETE FROM mv_base_a WHERE (i,j) = (1,10); +SELECT * FROM mv_ivm_min; + +SET search_path TO myschema,public,pg_catalog; +DELETE FROM public.mv_base_a WHERE (i,j) = (2,20); +SELECT * FROM mv_ivm_min; +ROLLBACK; + +-- aggregate views with column names specified +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +INSERT INTO mv_base_a VALUES (1,100), (2,200), (3,300); +UPDATE mv_base_a SET j = 2000 WHERE (i,j) = (2,20); +DELETE FROM mv_base_a WHERE (i,j) = (3,30); +SELECT * FROM mv_ivm_agg ORDER BY 1,2; +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_agg(a,b,c) AS SELECT i, SUM(j) FROM mv_base_a GROUP BY i; +ROLLBACK; + +-- support self join view and multiple change on the same table +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1, 10), (2, 20), (3, 30); +CREATE INCREMENTAL MATERIALIZED VIEW mv_self(v1, v2) AS + SELECT t1.v, t2.v FROM base_t AS t1 JOIN base_t AS t2 ON t1.i = t2.i; +SELECT * FROM mv_self ORDER BY v1; +INSERT INTO base_t VALUES (4,40); +DELETE FROM base_t WHERE i = 1; +UPDATE base_t SET v = v*10 WHERE i=2; +SELECT * FROM mv_self ORDER BY v1; +WITH + ins_t1 AS (INSERT INTO base_t VALUES (5,50) RETURNING 1), + ins_t2 AS (INSERT INTO base_t VALUES (6,60) RETURNING 1), + upd_t AS (UPDATE base_t SET v = v + 100 RETURNING 1), + dlt_t AS (DELETE FROM base_t WHERE i IN (4,5) RETURNING 1) +SELECT NULL; +SELECT * FROM mv_self ORDER BY v1; + +--- with sub-transactions +SAVEPOINT p1; +INSERT INTO base_t VALUES (7,70); +RELEASE SAVEPOINT p1; +INSERT INTO base_t VALUES (7,77); +SELECT * FROM mv_self ORDER BY v1, v2; + +ROLLBACK; + +-- support simultaneous table changes +BEGIN; +CREATE TABLE base_r (i int, v int); +CREATE TABLE base_s (i int, v int); +INSERT INTO base_r VALUES (1, 10), (2, 20), (3, 30); +INSERT INTO base_s VALUES (1, 100), (2, 200), (3, 300); +CREATE INCREMENTAL MATERIALIZED VIEW mv(v1, v2) AS + SELECT r.v, s.v FROM base_r AS r JOIN base_s AS s USING(i); +SELECT * FROM mv ORDER BY v1; +WITH + ins_r AS (INSERT INTO base_r VALUES (1,11) RETURNING 1), + ins_r2 AS (INSERT INTO base_r VALUES (3,33) RETURNING 1), + ins_s AS (INSERT INTO base_s VALUES (2,222) RETURNING 1), + upd_r AS (UPDATE base_r SET v = v + 1000 WHERE i = 2 RETURNING 1), + dlt_s AS (DELETE FROM base_s WHERE i = 3 RETURNING 1) +SELECT NULL; +SELECT * FROM mv ORDER BY v1; +ROLLBACK; + +-- support foreign reference constraints +BEGIN; +CREATE TABLE ri1 (i int PRIMARY KEY); +CREATE TABLE ri2 (i int PRIMARY KEY REFERENCES ri1(i) ON UPDATE CASCADE ON DELETE CASCADE, v int); +INSERT INTO ri1 VALUES (1),(2),(3); +INSERT INTO ri2 VALUES (1),(2),(3); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ri(i1, i2) AS + SELECT ri1.i, ri2.i FROM ri1 JOIN ri2 USING(i); +SELECT * FROM mv_ri ORDER BY i1; +UPDATE ri1 SET i=10 where i=1; +DELETE FROM ri1 WHERE i=2; +SELECT * FROM mv_ri ORDER BY i2; +ROLLBACK; + +-- views including NULL +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (1,10),(2, NULL); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +SELECT * FROM mv ORDER BY i; +UPDATE base_t SET v = 20 WHERE i = 2; +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +BEGIN; +CREATE TABLE base_t (i int); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT * FROM base_t; +SELECT * FROM mv ORDER BY i; +INSERT INTO base_t VALUES (1),(NULL); +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (1, 10), (1, 20); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, sum(v) FROM base_t GROUP BY i; +SELECT * FROM mv ORDER BY i; +UPDATE base_t SET v = v * 10; +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +BEGIN; +CREATE TABLE base_t (i int, v int); +INSERT INTO base_t VALUES (NULL, 1), (NULL, 2), (NULL, 3), (NULL, 4), (NULL, 5); +CREATE INCREMENTAL MATERIALIZED VIEW mv AS SELECT i, min(v), max(v) FROM base_t GROUP BY i; +SELECT * FROM mv ORDER BY i; +DELETE FROM base_t WHERE v = 1; +SELECT * FROM mv ORDER BY i; +DELETE FROM base_t WHERE v = 3; +SELECT * FROM mv ORDER BY i; +DELETE FROM base_t WHERE v = 5; +SELECT * FROM mv ORDER BY i; +ROLLBACK; + +-- IMMV containing user defined type +BEGIN; + +CREATE TYPE mytype; +CREATE FUNCTION mytype_in(cstring) + RETURNS mytype AS 'int4in' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_out(mytype) + RETURNS cstring AS 'int4out' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE TYPE mytype ( + LIKE = int4, + INPUT = mytype_in, + OUTPUT = mytype_out +); + +CREATE FUNCTION mytype_eq(mytype, mytype) + RETURNS bool AS 'int4eq' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_lt(mytype, mytype) + RETURNS bool AS 'int4lt' + LANGUAGE INTERNAL STRICT IMMUTABLE; +CREATE FUNCTION mytype_cmp(mytype, mytype) + RETURNS integer AS 'btint4cmp' + LANGUAGE INTERNAL STRICT IMMUTABLE; + +CREATE OPERATOR = ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_eq); +CREATE OPERATOR < ( + leftarg = mytype, rightarg = mytype, + procedure = mytype_lt); + +CREATE OPERATOR CLASS mytype_ops + DEFAULT FOR TYPE mytype USING btree AS + OPERATOR 1 <, + OPERATOR 3 = , + FUNCTION 1 mytype_cmp(mytype,mytype); + +CREATE TABLE t_mytype (x mytype); +CREATE INCREMENTAL MATERIALIZED VIEW mv_mytype AS + SELECT * FROM t_mytype; +INSERT INTO t_mytype VALUES ('1'::mytype); +SELECT * FROM mv_mytype; + +ROLLBACK; + +-- outer join is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i; +-- CTE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i; +-- contain system column +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm01 AS SELECT i,j,xmin FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm02 AS SELECT i,j FROM mv_base_a WHERE xmin = '610'; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT i,j,xmin::text AS x_min FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm06 AS SELECT i,j,xidsend(xmin) AS x_min FROM mv_base_a; +-- contain subquery +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 ); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm04 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT * FROM mv_base_b) b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a; +-- contain ORDER BY +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k; +-- contain HAVING +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) GROUP BY i,j,k HAVING SUM(i) > 5; + +-- contain view or materialized view +CREATE VIEW b_view AS SELECT i,k FROM mv_base_b; +CREATE MATERIALIZED VIEW b_mview AS SELECT i,k FROM mv_base_b; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT a.i,a.j FROM mv_base_a a,b_view b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm08 AS SELECT a.i,a.j FROM mv_base_a a,b_mview b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm09 AS SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm10 AS SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 5; + +-- contain mutable functions +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm12 AS SELECT i,j FROM mv_base_a WHERE i = random()::int; + +-- LIMIT/OFFSET is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm13 AS SELECT i,j FROM mv_base_a LIMIT 10 OFFSET 5; + +-- DISTINCT ON is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm14 AS SELECT DISTINCT ON(i) i, j FROM mv_base_a; + +-- TABLESAMPLE clause is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm15 AS SELECT i, j FROM mv_base_a TABLESAMPLE SYSTEM(50); + +-- window functions are not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm16 AS SELECT *, cume_dist() OVER (ORDER BY i) AS rank FROM mv_base_a; + +-- aggregate function with some options is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm17 AS SELECT COUNT(*) FILTER(WHERE i < 3) FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm18 AS SELECT COUNT(DISTINCT i) FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm19 AS SELECT array_agg(j ORDER BY i DESC) FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm20 AS SELECT i,SUM(j) FROM mv_base_a GROUP BY GROUPING SETS((i),()); + +-- inheritance parent is not supported +BEGIN; +CREATE TABLE parent (i int, v int); +CREATE TABLE child_a(options text) INHERITS(parent); +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm21 AS SELECT * FROM parent; +ROLLBACK; + +-- UNION statement is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm22 AS SELECT i,j FROM mv_base_a UNION ALL SELECT i,k FROM mv_base_b;; + +-- empty target list is not allowed with IVM +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm25 AS SELECT FROM mv_base_a; + +-- FOR UPDATE/SHARE is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm26 AS SELECT i,j FROM mv_base_a FOR UPDATE; + +-- tartget list cannot contain ivm column that start with '__ivm' +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm28 AS SELECT i AS "__ivm_count__" FROM mv_base_a; + +-- expressions specified in GROUP BY must appear in the target list. +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm29 AS SELECT COUNT(i) FROM mv_base_a GROUP BY i; + +-- experssions containing an aggregate is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm30 AS SELECT sum(i)*0.5 FROM mv_base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm31 AS SELECT sum(i)/sum(j) FROM mv_base_a; + +-- VALUES is not supported +CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_only_values1 AS values(1); + +-- views containing base tables with Row Level Security +DROP USER IF EXISTS regress_ivm_admin; +DROP USER IF EXISTS regress_ivm_user; +CREATE USER regress_ivm_admin; +CREATE USER regress_ivm_user; + +--- create a table with RLS +SET SESSION AUTHORIZATION regress_ivm_admin; +CREATE TABLE rls_tbl(id int, data text, owner name); +INSERT INTO rls_tbl VALUES + (1,'foo','regress_ivm_user'), + (2,'bar','postgres'); +CREATE TABLE num_tbl(id int, num text); +INSERT INTO num_tbl VALUES + (1,'one'), + (2,'two'), + (3,'three'), + (4,'four'), + (5,'five'), + (6,'six'); + +--- Users can access only their own rows +CREATE POLICY rls_tbl_policy ON rls_tbl FOR SELECT TO PUBLIC USING(owner = current_user); +ALTER TABLE rls_tbl ENABLE ROW LEVEL SECURITY; +GRANT ALL on rls_tbl TO PUBLIC; +GRANT ALL on num_tbl TO PUBLIC; + +--- create a view owned by regress_ivm_user +SET SESSION AUTHORIZATION regress_ivm_user; + +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls AS SELECT * FROM rls_tbl; +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; +RESET SESSION AUTHORIZATION; + +--- inserts rows owned by different users +INSERT INTO rls_tbl VALUES + (3,'baz','regress_ivm_user'), + (4,'qux','postgres'); +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + +--- combination of diffent kinds of commands +WITH + i AS (INSERT INTO rls_tbl VALUES(5,'quux','postgres'), (6,'corge','regress_ivm_user')), + u AS (UPDATE rls_tbl SET owner = 'postgres' WHERE id = 1), + u2 AS (UPDATE rls_tbl SET owner = 'regress_ivm_user' WHERE id = 2) +SELECT; +SELECT id, data, owner FROM ivm_rls ORDER BY 1,2,3; + +--- +SET SESSION AUTHORIZATION regress_ivm_user; +CREATE INCREMENTAL MATERIALIZED VIEW ivm_rls2 AS SELECT * FROM rls_tbl JOIN num_tbl USING(id); +RESET SESSION AUTHORIZATION; + +WITH + x AS (UPDATE rls_tbl SET data = data || '_2' where id in (3,4)), + y AS (UPDATE num_tbl SET num = num || '_2' where id in (3,4)) +SELECT; +SELECT * FROM ivm_rls2 ORDER BY 1,2,3; + +-- automatic index creation +CREATE TABLE base_a (i int primary key, j int); +CREATE TABLE base_b (i int primary key, j int); + +--- group by: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx1 AS SELECT i, sum(j) FROM base_a GROUP BY i; + +--- distinct: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx2 AS SELECT DISTINCT j FROM base_a; + +--- with all pkey columns: create an index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx3(i_a, i_b) AS SELECT a.i, b.i FROM base_a a, base_b b; + +--- missing some pkey columns: no index +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx4 AS SELECT j FROM base_a; +CREATE INCREMENTAL MATERIALIZED VIEW mv_idx5 AS SELECT a.i, b.j FROM base_a a, base_b b; + +-- cleanup + +DROP TABLE rls_tbl CASCADE; +DROP TABLE num_tbl CASCADE; +DROP USER regress_ivm_user; +DROP USER regress_ivm_admin; + +DROP TABLE mv_base_b CASCADE; +DROP TABLE mv_base_a CASCADE; -- 2.25.1 --Multipart=_Sun__31_Mar_2024_22_59_31_+0900_msknEviJj08_wgqO Content-Type: text/x-diff; name="v32-0011-Add-documentations-about-Incremental-View-Mainte.patch" Content-Disposition: attachment; filename="v32-0011-Add-documentations-about-Incremental-View-Mainte.patch" Content-Transfer-Encoding: 7bit ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-06-16 11:54 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-06-16 11:54 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9407c357f27..fa5a53d04ff 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9ab74c8df0a..2af586669ae 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -689,6 +689,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1376,22 +1378,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1408,9 +1404,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1495,9 +1489,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1524,7 +1516,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1537,8 +1529,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -3926,27 +3918,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4034,11 +4018,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 2debadd86ed..28b34fd4387 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -503,13 +503,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -528,15 +526,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -546,7 +538,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -670,11 +662,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1207,7 +1196,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 38f9ffcd04f..c0f6217caa6 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23245,9 +23245,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23379,18 +23377,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23427,11 +23416,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index a4abb29cf64..8922d713916 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4d5abbaca7..90234c3f736 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1325,6 +1325,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v01-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
* [PATCH 5/8] Simplify the way restrictions are imposed on index functions. @ 2026-07-15 08:37 Antonin Houska <ah@cybertec.at> 0 siblings, 0 replies; 604+ messages in thread From: Antonin Houska @ 2026-07-15 08:37 UTC (permalink / raw) Whenever we expect possible execution of index functions, we need to make sure that they execute with the appropriate privileges. Also, the core should not see (and use) values of GUC parameters introduced by the index functions. This patch introduces functions enable_index_build_security() and disable_index_build_security() which make the security measures less verbose. It's needed for the upcoming enhancements of REPACK (CONCURRENTLY), but looks like useful refactoring anyway. --- src/backend/access/brin/brin.c | 32 ++++---------- src/backend/catalog/index.c | 72 ++++++++++++++++++-------------- src/backend/commands/analyze.c | 21 +++------- src/backend/commands/indexcmds.c | 49 +++++++--------------- src/backend/commands/repack.c | 24 +++-------- src/backend/commands/tablecmds.c | 24 +++-------- src/backend/commands/vacuum.c | 23 +++------- src/include/catalog/index.h | 10 +++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 95 insertions(+), 161 deletions(-) diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c index bdb30752e09..2359bffa518 100644 --- a/src/backend/access/brin/brin.c +++ b/src/backend/access/brin/brin.c @@ -1391,10 +1391,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) Oid heapoid; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; double numSummarized = 0; + IndexBuildSecurity ibsec; if (RecoveryInProgress()) ereport(ERROR, @@ -1420,27 +1418,12 @@ brin_summarize_range(PG_FUNCTION_ARGS) heapRel = table_open(heapoid, ShareUpdateExclusiveLock); /* - * Autovacuum calls us. For its benefit, switch to the table owner's - * userid, so that any index functions are run as that user. Also - * lock down security-restricted operations and arrange to make GUC - * variable changes local to this command. This is harmless, albeit - * unnecessary, when called from SQL, because we fail shortly if the - * user does not own the index. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); } else - { heapRel = NULL; - /* Set these just to suppress "uninitialized variable" warnings */ - save_userid = InvalidOid; - save_sec_context = -1; - save_nestlevel = -1; - } indexRel = index_open(indexoid, ShareUpdateExclusiveLock); @@ -1453,7 +1436,8 @@ brin_summarize_range(PG_FUNCTION_ARGS) RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ - if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, save_userid)) + if (heapRel != NULL && !object_ownercheck(RelationRelationId, indexoid, + ibsec.userid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, RelationGetRelationName(indexRel)); @@ -1477,11 +1461,9 @@ brin_summarize_range(PG_FUNCTION_ARGS) errmsg("index \"%s\" is not valid", RelationGetRelationName(indexRel)))); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); index_close(indexRel, ShareUpdateExclusiveLock); table_close(heapRel, ShareUpdateExclusiveLock); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 81bba4beac7..8eabe232d1e 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1504,11 +1504,9 @@ index_concurrently_build(Oid heapRelationId, Oid indexRelationId) { Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation indexRelation; IndexInfo *indexInfo; + IndexBuildSecurity ibsec; /* This had better make sure that a snapshot is active */ Assert(ActiveSnapshotSet()); @@ -1517,15 +1515,9 @@ index_concurrently_build(Oid heapRelationId, heapRel = table_open(heapRelationId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); indexRelation = index_open(indexRelationId, RowExclusiveLock); @@ -1542,11 +1534,8 @@ index_concurrently_build(Oid heapRelationId, /* Now build the index */ index_build(heapRel, indexRelation, indexInfo, false, true, true); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close both the relations, but keep the locks */ table_close(heapRel, NoLock); @@ -3375,9 +3364,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) IndexInfo *indexInfo; IndexVacuumInfo ivinfo; ValidateIndexState state; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; { const int progress_index[] = { @@ -3399,15 +3386,9 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) heapRelation = table_open(heapId, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRelation->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRelation->rd_rel->relowner, &ibsec); indexRelation = index_open(indexId, RowExclusiveLock); @@ -3486,11 +3467,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) "validate_index found %.0f heap tuples, %.0f index tuples; inserted %.0f missing tuples", state.htups, state.itups, state.tups_inserted); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Close rels, but keep locks */ index_close(indexRelation, NoLock); @@ -4115,6 +4093,36 @@ reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, return result; } +/* + * Before building an index, witch to the table owner's userid, so that any + * index functions are run as that user. Also lock down security-restricted + * operations and arrange to make GUC variable changes local to this command. + * + * Information needed later by disable_index_build_security() is stored in + * *sec. + */ +void +enable_index_build_security(Oid userid, IndexBuildSecurity *sec) +{ + GetUserIdAndSecContext(&sec->userid, &sec->sec_context); + SetUserIdAndSecContext(userid, + sec->sec_context | SECURITY_RESTRICTED_OPERATION); + sec->nestlevel = NewGUCNestLevel(); + RestrictSearchPath(); +} + +/* + * Undo what enable_index_build_security() did. + */ +void +disable_index_build_security(IndexBuildSecurity *sec) +{ + /* Roll back any GUC changes executed by index functions */ + AtEOXact_GUC(false, sec->nestlevel); + + /* Restore userid and security context */ + SetUserIdAndSecContext(sec->userid, sec->sec_context); +} /* ---------------------------------------------------------------- * System index reindexing support diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f66e80b757c..dc2ad77ef9a 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -328,14 +328,12 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, PGRUsage ru0; TimestampTz starttime = 0; MemoryContext caller_context; - Oid save_userid; - int save_sec_context; - int save_nestlevel; WalUsage startwalusage = pgWalUsage; BufferUsage startbufferusage = pgBufferUsage; BufferUsage bufferusage; PgStat_Counter startreadtime = 0; PgStat_Counter startwritetime = 0; + IndexBuildSecurity ibsec; verbose = (params->options & VACOPT_VERBOSE) != 0; instrument = (verbose || (AmAutoVacuumWorkerProcess() && @@ -361,15 +359,9 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, caller_context = MemoryContextSwitchTo(anl_context); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(onerel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(onerel->rd_rel->relowner, &ibsec); /* * When verbose or autovacuum logging is used, initialize a resource usage @@ -858,11 +850,8 @@ do_analyze_rel(Relation onerel, const VacuumParams *params, } } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* Restore current context and release memory */ MemoryContextSwitchTo(caller_context); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 713bb5d10f1..5bc28c94139 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -699,6 +699,8 @@ DefineIndex(ParseState *pstate, * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations. We * already arranged to make GUC variable changes local to this command. + * + * XXX Use enable_index_build_security()? */ GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context); SetUserIdAndSecContext(rel->rd_rel->relowner, @@ -1386,22 +1388,16 @@ DefineIndex(ParseState *pstate, { Oid childRelid = part_oids[i]; Relation childrel; - Oid child_save_userid; - int child_save_sec_context; - int child_save_nestlevel; List *childidxs; ListCell *cell; AttrMap *attmap; bool found = false; + IndexBuildSecurity child_ibsec; childrel = table_open(childRelid, lockmode); - GetUserIdAndSecContext(&child_save_userid, - &child_save_sec_context); - SetUserIdAndSecContext(childrel->rd_rel->relowner, - child_save_sec_context | SECURITY_RESTRICTED_OPERATION); - child_save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(childrel->rd_rel->relowner, + &child_ibsec); /* * Don't try to create indexes on foreign tables, though. Skip @@ -1418,9 +1414,7 @@ DefineIndex(ParseState *pstate, errdetail("Table \"%s\" contains partitions that are foreign tables.", RelationGetRelationName(rel)))); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, lockmode); continue; } @@ -1505,9 +1499,7 @@ DefineIndex(ParseState *pstate, } list_free(childidxs); - AtEOXact_GUC(false, child_save_nestlevel); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + disable_index_build_security(&child_ibsec); table_close(childrel, NoLock); /* @@ -1534,7 +1526,7 @@ DefineIndex(ParseState *pstate, * Recurse as the starting user ID. Callee will use that * for permission checks, then switch again. */ - Assert(GetUserId() == child_save_userid); + Assert(GetUserId() == child_ibsec.userid); SetUserIdAndSecContext(root_save_userid, root_save_sec_context); childAddr = @@ -1547,8 +1539,8 @@ DefineIndex(ParseState *pstate, is_alter_table, check_rights, check_not_in_use, skip_build, quiet); - SetUserIdAndSecContext(child_save_userid, - child_save_sec_context); + SetUserIdAndSecContext(child_ibsec.userid, + child_ibsec.sec_context); /* * Check if the index just created is valid or not, as it @@ -4051,27 +4043,19 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein Oid newIndexId; Relation indexRel; Relation heapRel; - Oid save_userid; - int save_sec_context; - int save_nestlevel; Relation newIndexRel; LockRelId *lockrelid; Oid tablespaceid; + IndexBuildSecurity ibsec; indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock); heapRel = table_open(indexRel->rd_index->indrelid, ShareUpdateExclusiveLock); /* - * Switch to the table owner's userid, so that any index functions are - * run as that user. Also lock down security-restricted operations - * and arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(heapRel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(heapRel->rd_rel->relowner, &ibsec); /* determine safety of this index for set_indexsafe_procflags */ idx->safe = (RelationGetIndexExpressions(indexRel) == NIL && @@ -4159,11 +4143,8 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein index_close(indexRel, NoLock); index_close(newIndexRel, NoLock); - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); table_close(heapRel, NoLock); diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 0bf19d07db5..1ad453a39a0 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -514,13 +514,11 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid tableOid = RelationGetRelid(OldHeap); Relation index; LOCKMODE lmode; - Oid save_userid; - int save_sec_context; - int save_nestlevel; bool verbose = ((params->options & CLUOPT_VERBOSE) != 0); bool recheck = ((params->options & CLUOPT_RECHECK) != 0); bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0); Oid ident_idx = InvalidOid; + IndexBuildSecurity ibsec; /* Determine the lock mode to use. */ lmode = RepackLockLevel(concurrent); @@ -539,15 +537,9 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, pgstat_progress_update_param(PROGRESS_REPACK_COMMAND, cmd); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(OldHeap->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(OldHeap->rd_rel->relowner, &ibsec); /* * Recheck that the relation is still what it was when we started. @@ -557,7 +549,7 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, * not-previously-clustered index. */ if (recheck && - !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid, + !cluster_rel_recheck(cmd, OldHeap, indexOid, GetUserId(), lmode, params->options)) goto out; @@ -681,11 +673,8 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, rebuild_relation(OldHeap, index, verbose, ident_idx); out: - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); pgstat_progress_end_command(); } @@ -1234,7 +1223,6 @@ rebuild_relation(Relation OldHeap, Relation index, bool verbose, } } - /* * Create the transient table that will be filled with new data during * CLUSTER, ALTER TABLE, and similar operations. The transient table diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cb93c3e935a..d691b317011 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23762,9 +23762,7 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, Oid defaultPartOid; Oid existingRelid; Oid ownerId = InvalidOid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; + IndexBuildSecurity ibsec; /* * Check ownership of merged partitions - partitions with different owners @@ -23896,18 +23894,9 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId); /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also, lockdown security-restricted operations and - * arrange to make GUC variable changes local to this command. - * - * Need to do it after determining the namespace in the - * createPartitionTable() call. + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(ownerId, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(ownerId, &ibsec); /* Copy data from merged partitions to the new partition. */ MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel); @@ -23944,11 +23933,8 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, /* Keep the lock until commit. */ table_close(newPartRel, NoLock); - /* Roll back any GUC changes executed by index functions. */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore the userid and security context. */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); } /* diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 38539a6fd3d..5d1cbc382fa 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -34,6 +34,7 @@ #include "access/tableam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" @@ -2017,10 +2018,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, LockRelId lockrelid; Oid priv_relid; Oid toast_relid; - Oid save_userid; - int save_sec_context; - int save_nestlevel; VacuumParams toast_vacuum_params; + IndexBuildSecurity ibsec; /* * This function scribbles on the parameters, so make a copy early to @@ -2270,16 +2269,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, toast_relid = InvalidOid; /* - * Switch to the table owner's userid, so that any index functions are run - * as that user. Also lock down security-restricted operations and - * arrange to make GUC variable changes local to this command. (This is - * unnecessary, but harmless, for lazy VACUUM.) + * Prevent index functions from doing what they are not supposed to. */ - GetUserIdAndSecContext(&save_userid, &save_sec_context); - SetUserIdAndSecContext(rel->rd_rel->relowner, - save_sec_context | SECURITY_RESTRICTED_OPERATION); - save_nestlevel = NewGUCNestLevel(); - RestrictSearchPath(); + enable_index_build_security(rel->rd_rel->relowner, &ibsec); /* * If PROCESS_MAIN is set (the default), it's time to vacuum the main @@ -2310,11 +2302,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params, table_relation_vacuum(rel, ¶ms, bstrategy); } - /* Roll back any GUC changes executed by index functions */ - AtEOXact_GUC(false, save_nestlevel); - - /* Restore userid and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); + /* Relax the restrictions imposed above. */ + disable_index_build_security(&ibsec); /* all done with this class, but hold lock until commit */ if (rel) diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 9aee8226347..dd9ae8119e5 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -172,6 +172,16 @@ extern void reindex_index(const ReindexStmt *stmt, Oid indexId, extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params); +typedef struct IndexBuildSecurity +{ + Oid userid; + int sec_context; + int nestlevel; +} IndexBuildSecurity; + +extern void enable_index_build_security(Oid userid, IndexBuildSecurity *sec); +extern void disable_index_build_security(IndexBuildSecurity *sec); + extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 25ac7079099..0ec534d15d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1327,6 +1327,7 @@ IndexAttachInfo IndexAttrBitmapKind IndexBuildCallback IndexBuildResult +IndexBuildSecurity IndexBulkDeleteCallback IndexBulkDeleteResult IndexClause -- 2.52.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=v02-0006-Use-separate-transactions-for-catalog-changes.patch ^ permalink raw reply [nested|flat] 604+ messages in thread
end of thread, other threads:[~2026-07-15 08:37 UTC | newest] Thread overview: 604+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2021-03-10 02:11 [PATCH v32 10/11] Add regression tests for Incremental View Maintenance Takuma Hoshiai <takuma.hoshiai@gmail.com> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-06-16 11:54 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at> 2026-07-15 08:37 [PATCH 5/8] Simplify the way restrictions are imposed on index functions. Antonin Houska <ah@cybertec.at>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox